#ifndef __PROCESSOR_H__
#define __PROCESSOR_H__

#include "base/AccessRepeater.h"
#include "base/DataRequest.h"
#include "base/Entity.h"
#include "base/StatisticsCollection.h"
#include "base/TrapGenerator.h"
#include "mem/InvalidateRequest.h"
#include "util/Logger.h"
#include "util/Pool.h"
#include "util/HashTable.h"

#define PRINT_BUFFER_SIZE 4096

class CPU;
class Checkpoint;
class SigHandler;
class Simulator;
class StatisticsCollection;
class SigContext;

extern Logger* quiesceLog;

typedef enum ProcStatus {
    running,         /* executing instructions */
    waiting,         /* i'm waiting for tcc_halt to finish */
    halted,          /* i'm not doing anything now */
} ProcStatus;

static const char* ProcStatusNames[] = {
    "running",
    "waiting",
    "halted",
};

class Access {
  public:
    NEW_DELETE_OPERATORS_H(Access,NoSuper);
    Access () {}
    Time clock;
    VA   PC;
};

class Processor : public Entity {
  public:
    NEW_DELETE_OPERATORS_H(Processor,Entity);
    Processor (Simulator* simulator, const char* name, uint id);
    ~Processor ();
    virtual void reset (VA startPC,
                        VA returnPC,
                        int argc,
                        Reg arg0,
                        Reg arg1,
                        Reg arg2,
                        Reg arg3);

    Result sendWordAccess (VA addr,
                           uint32* res,
                           WordAccessType type,
                           bool atomic=false);

    CPU* getCPU () {
        return cpu;
    }

    SigHandler* getSigHandler () {
        return sigHandler;
    }
    TrapGenerator* getTrapGenerator () {
        return trapGenerator;
    }

    uint getID () {
        return _id;
    }
    bool isRunning ();
    bool isHalted ();
    bool isRepeating ();
    void clearRepeater ();

    /**
      this function is used by the simulator to determine if the Processor
      can take an asynch signal right now.  Transactional Procesors should
      override this and return false if they are in a transaction.
     */
    virtual bool canHandleSignal ();

    /**
      serialize returns true if we have successfully taken the "one
      big lock" and blocked others out so we can do something special
      like fast forward or run a system call or ...

      This simply quiesces by default, but on transactional systems
      it violates other running transactions, validating to ensure
      than the serializing processor can do whatever it needs to do.
     */
    virtual bool serialize ();
    /**
      Allow other Processors to proceed
     */
    virtual void unserialize ();

    virtual void tick ();
    virtual void halt ();

    virtual bool isQuiescing ();
    virtual void sendQuiesceRequest ();
    virtual void hookQuiesce ();
    bool cancelDataRequest ();
    void clearDataRequest ();
    void rawFlushCaches (VA start, size_t len);

    virtual Result handle_Response (Packet* r);
    Result handle_QuiesceRequest (Packet* r);
    Result handle_UnQuiesce (Packet* r);
    virtual Result handle_QuiesceResponse (Packet* r);

    // Statistics
    virtual void hookPrintStatistics() {};
    virtual void countVotes ();
    virtual StatisticsCollection* getStatistics () {
        return statistics;
    }
    int incStatsLockNest (int delta=1) {
        return (stats_lock_nest += delta);
    }
    int decStatsLockNest (int delta=-1) {
        return incStatsLockNest(delta);
    }

    void (Logger::* getPrintFunction())(char*, ...);

    static bool isa_ext_get_myid (Processor* P,
                                  Reg arg0,
                                  Reg arg1,
                                  Reg* result);
    static bool isa_ext_goto_real (Processor* P,
                                   Reg arg0,
                                   Reg arg1,
                                   Reg* result);
    static bool isa_ext_set_print_fmt (Processor* P,
                                       Reg arg0,
                                       Reg arg1,
                                       Reg* result);
    static bool isa_ext_set_print_argv (Processor* P,
                                        Reg arg0,
                                        Reg arg1,
                                        Reg* result);
    static bool isa_ext_set_print_warning (Processor* P,
                                           Reg arg0,
                                           Reg arg1,
                                           Reg* result);
    static bool isa_ext_print_atomic (Processor* P,
                                      Reg arg0,
                                      Reg arg1,
                                      Reg* result);
    static bool isa_ext_print_atomic_buffer (Processor* P,
                                             Reg arg0,
                                             Reg arg1,
                                             Reg* result);
    static bool isa_ext_set_print (Processor* P,
                                   Reg arg0,
                                   Reg arg1,
                                   Reg* result);
    static bool isa_ext_set_ignore_progress (Processor* P,
                                             Reg arg0,
                                             Reg arg1,
                                             Reg* result);
    static bool isa_ext_setjmp (Processor* P,
                                Reg arg0,
                                Reg arg1,
                                Reg* result);
    static bool isa_ext_longjmp (Processor* P,
                                 Reg arg0,
                                 Reg arg1,
                                 Reg* result);
    static bool isa_ext_line_move (Processor* P,
                                   Reg arg0, // src
                                   Reg arg1, // dest
                                   Reg* result);

    // For stats
    static bool isa_ext_stat_lock    (Processor* P, Reg arg0, Reg arg1, Reg* result);
    static bool isa_ext_stat_unlock  (Processor* P, Reg arg0, Reg arg1, Reg* result);
    static bool isa_ext_stat_bar     (Processor* P, Reg arg0, Reg arg1, Reg* result);
    static bool isa_ext_stat_bar_end (Processor* P, Reg arg0, Reg arg1, Reg* result);
    static bool isa_ext_stat_enable  (Processor* P, Reg arg0, Reg arg1, Reg* result);
    static bool isa_ext_stat_disable (Processor* P, Reg arg0, Reg arg1, Reg* result);


    static char* appendSymbolInformation(char* index, VA addr, int padding = 0);

    Counter* statStallTime;
    Counter* statInstructionsExecuted;
    Counter* statUnaccounted;

    uint64 getInstructionsExecuted () {
        return statInstructionsExecuted->peek();
    }

    Logger* getInstructionTrace () {
        return itrace;
    }

    struct config {
        uint initial_stack_size;
        uint stack_alignment_size;
        bool stack_debug;

        bool print_master;
        bool print_inst_on_commit;
        bool print_inst_on_fetch;
        bool print_registers;
        bool print_all_registers;
        bool print_memory;
        bool print_symbols;

        bool transaction_trace;
    } cfg;

    bool hb_print;
    uint64 hb_ival;
    bool ips_print;
    uint ips_ival;

    // gdb
    bool firstCycle;
    bool getMemory (VA vAddr, uint nbytes, uint8* buf);
    const char* getStatus () {
        return ProcStatusNames[status];
    }

    /**
      watchdogTimeSinceProgress returns time since the PC was changed
     */
    Time watchdogTimeSinceProgress ();

    /**
      getWatchdogIgnoreProcess checks if this process is involved in
      watchdog detection
     */
    bool getWatchdogIgnoreProgress () { 
        return watchdogIgnoreProgress;
    }

    /** 
      inPrivateBounds tries to determine if the specified range
      falls is private to the current processor. This is only a
      hueristc meant to detect unintentional "raw flushing" if
      shared speculative data.
     */
    bool inPrivateBounds (VA addr, size_t len);

    void clearInstructionState ();

    void printStatisticsCollection (FILE* statFile);

    virtual void hookScheduleThreadSignal (int procID,
                                           int signum,
                                           SigContext* sigContext);

    Bucket* bktMemoryStall;
    Bucket* bktCyclesBusy;
    Bucket* bktCyclesHalted;
    Bucket* bktCyclesLock;
    Bucket* bktCyclesBarrier;
    Counter* ctrStatsEnable;
    Counter* ctrL1Accesses;

  protected:
    static void initializeClass ();

    char*      print_fmt;
    int        print_argv[8];
    int        print_warning;
    char       print_buffer[PRINT_BUFFER_SIZE];
    int        print_buffer_index;

    ProcStatus status;

  private:
    /** 
      Processor unique ID, 0 indexed
     */
    int _id;

    /** 
      buildStack is responsible for allocated the stack and setting up related fields. 
     */
    Byte* buildStack ();

    /** 
      pointer to stack memory allocated via CALLOC
     */
    Byte* stack_unaligned_start;

    /** 
      pointer to top of aligned stack for use by CPU
     */
    Byte* stack_aligned_end;
  protected:

    CPU*       cpu;

    // For setjmp/longjmp
    Checkpoint* jmpbufPtr;
    int         jmpval;

    DataRequest* dataRequest;
    bool         dataRequestDone;
    bool         dataRequestFailed;

    StatisticsCollection* statistics;
    int          stats_lock_nest;
    int          sample_delta;

    AccessRepeater repeater;

    SigHandler*    sigHandler;
    TrapGenerator* trapGenerator;

    Logger*        programLog;
    Logger*        itrace;

    virtual void   hookElect (Bucket*);
    virtual void   hookWordAccess ();
    virtual void   hookWordAccessSuccess (WordAccessType type);
    void           getconfig ();
    void           sampleState (Bucket*);
    void           initStats ();
    Result        accessTLB(VA addr, WordAccessType type);
    virtual Result        hookTLBAccess(VA addr, WordAccessType type);

    /**
      watchdogMadeProgress is called to see if the PC has changed
     */
    bool watchdogMadeProgress ();

    void printHeartbeat ();
    void printIPS ();
    uint64 last_inst;
    uint64 last_ips_inst;
    time_t last_ips_sec;

    virtual void hookQuiesced();
    virtual bool isReadyToQuiesce ();
    virtual void hookInstructionCompletion();

    bool getQuiesce () {
        return _quiesce;
    }
    void setQuiesce (bool b) {
        quiesceLog->d("%d: setQuiesce(%d)\n", _id, b);
        _quiesce = b;
    }
    bool getQuiesceAfterThisInstruction () {
        return _quiesceAfterThisInstruction;
    }
    void setQuiesceAfterThisInstruction (bool b) {
        quiesceLog->d("%d: setQuiesceAfterThisInstruction(%d)\n", _id, b);
        _quiesceAfterThisInstruction = b;
    }
    bool getQuiescedActive () {
        return _quiescedActive;
    }
    void setQuiescedActive (bool b) {
        quiesceLog->d("%d: setQuiescedActive(%d)\n", _id, b);
        _quiescedActive = b;
    }

  private:
    static bool initializedClass;

    /** 
      watchdogIgnoreProgress allows some processors to be excluded
      from forward progress measurement. used in JikesRVM to ignore
      "progress" of noop timer thread.
     */
    bool watchdogIgnoreProgress;
    /** 
      watchdogLastPC is the active PC of this processor.
     */
    VA watchdogLastPC;
    /** 
      watchdogLastSimclock is the simblock the watchdogLastPC was
      last updated.
     */
    uint64 watchdogLastSimclock;

    bool _quiesce;
    bool _quiesceAfterThisInstruction;
    bool _quiescedActive;
};

void deleteAccess (VA addr, Access* a);
#endif
